
#ifndef META_LOOPER_HPP
#define META_LOOPER_HPP

#include "meta_objects.hpp"
#include "..\meta_invoke\meta_invoke.hpp"

namespace meta_loop {
    using namespace meta_objects;

    //Note: All template parameters are meta objects
	namespace meta_looper_detail {
		template<bool, class Condition, class OBJ, class Generator = meta_empty_o>
		struct meta_looper_impl
		{
			
			template<class ...Args> struct apply {

				//transfer current obj to  condition_obj to judge
				//transfer different context based on types of meta_object
				using _continue_t = typename meta_invoke<meta_transfer_object<OBJ, Condition>>::type;
				static const bool _continue_ = _continue_t::value;

				//invoke generator object if condition is true
				using generator_stage_o = meta_invoke<invoke_if<_continue_>, Generator, Args...>;

				//invoke Obj object if condition is true
				using result_stage_o = meta_invoke<invoke_object_if<_continue_>, OBJ, generator_stage_o>;

				//recursively loop for result
				using track_apply_t = meta_invoke<invoke_if<_continue_>, meta_looper_impl<
					_continue_,
					Condition,
					result_stage_o,
					generator_stage_o
				>, Args...>;
				using type = typename track_apply_t::type;

				template<class ...arg_types>
				static constexpr auto for_each(auto&& f, arg_types &&...args) {
					if constexpr (_continue_) {
						std::invoke(f, typename result_stage_o::type{}, std::forward<arg_types>(args)...);
						return track_apply_t::for_each(f, std::forward<arg_types>(args)...);
					}
				}

				template<class first_arg_type, class ...arg_types>
				static constexpr auto for_each_forward(auto&& f, first_arg_type&& first, arg_types &&...args) {
					if constexpr (_continue_) {
						std::invoke(f, typename result_stage_o::type{}, std::forward<first_arg_type>(first));
						if constexpr(sizeof ...(arg_types))
							return track_apply_t::for_each_forward(f, std::forward<arg_types>(args)...);
					}
				}
			};
		};

		
		template<class Cond, class MO, class Generator> struct meta_looper_impl<false, Cond, MO, Generator>
		{
			static constexpr bool _continue_ = false;
			using type = typename MO::type;
			template<class ...arg_types>
			static constexpr auto for_each(auto&& f, arg_types &&...args) {
				std::invoke(f, typename MO::type{}, std::forward<arg_types>(args)...);
			}
			template<class first_arg_type, class ...arg_types>
			static constexpr auto for_each_forward(auto&& f, first_arg_type&& first, arg_types &&...args) {
				std::invoke(f, typename MO::type{}, std::forward<first_arg_type>(first));
			}
		};
	}

	template<class C, class O, class G, class ...ARG_Tys>
	using meta_looper_t = typename meta_invoke<meta_looper_detail::meta_looper_impl<true, C, O, G>, ARG_Tys...>::type;

	template<class C, class O, class G>
    using meta_looper = meta_looper_detail::meta_looper_impl<true, C, O, G>;
}
#endif // !META_LOOPER_HPP
